package com.zc.nettycilent;

import com.zc.nettycilent.connect.MiiClientConnect;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@SpringBootApplication
@Slf4j
public class NettyCilentApplication extends ChannelInitializer<SocketChannel> implements CommandLineRunner {
    private static MiiClientConnect miiClientConnect;
    public static void main(String[] args) {
        SpringApplication.run(NettyCilentApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .group(workGroup)
                .channel(NioSocketChannel.class)
                // 添加连接选项以更快检测断开连接
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(this);
        InetSocketAddress socketAddress = new InetSocketAddress("192.168.150.8", 8899);
        MiiClientConnect mc = new MiiClientConnect(bootstrap, 5000) {

            @Override
            protected void afterSuccess() {
                log.info("ok le ");
            }
        };

        miiClientConnect = mc;

        mc.connect(socketAddress);

    }

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline p = ch.pipeline();
        p.addLast(miiClientConnect);
        p.addLast(new IdleStateHandler(60000, 0, 5000, TimeUnit.MILLISECONDS));
        p.addLast(new FixedLengthFrameDecoder(5));
        p.addLast(new StringDecoder(Charset.forName("gbk")));
        p.addLast(new StringEncoder(Charset.forName("gbk")));
        p.addLast(new SimpleChannelInboundHandler<String>(){
            @Override
            protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
                log.info("channelRead0...:{}", s);
                channelHandlerContext.channel().writeAndFlush("中国:" + s);
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                log.info("channelReadComplete...");
            }

            @Override
            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                if (evt instanceof IdleStateEvent) {
                    IdleStateEvent event = (IdleStateEvent) evt;
//                    if (event.state() == IdleState.ALL_IDLE) {
//                        // 发送心跳检测
//                        log.info("发送心跳检测");
//                        ctx.writeAndFlush("PING");
//                    }
                    if (event.state() == IdleState.READER_IDLE) {
                        // 读空闲超时，说明在指定时间内没有收到服务端的任何数据
                        // 我们认为连接已经不稳定或断开，主动关闭连接
                        System.out.println("读空闲超时，关闭连接: " + ctx.channel());
                        ctx.close();
                    } else if (event.state() == IdleState.WRITER_IDLE) {
                        // 写空闲超时，说明有一段时间没有发送数据了。
                        // 为了避免连接因空闲被服务端关闭，我们主动发送一个心跳包维持连接
                        System.out.println("写空闲，发送心跳包: " + ctx.channel());
                        ctx.writeAndFlush("PING");
                    }
                } else {
                    super.userEventTriggered(ctx, evt);
                }
            }
        });

    }
}
